home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 41 / Amiga Format CD41 (1999-06)(Future Publishing)(GB)[!][issue 1999-07].iso / -seriously_amiga- / programming / other / scm / slib / printf.scm < prev    next >
Text File  |  1999-04-19  |  16KB  |  542 lines

  1. ;;;; "printf.scm" Implementation of standard C functions for Scheme
  2. ;;; Copyright (C) 1991-1993, 1996 Aubrey Jaffer.
  3. ;
  4. ;Permission to copy this software, to redistribute it, and to use it
  5. ;for any purpose is granted, subject to the following restrictions and
  6. ;understandings.
  7. ;
  8. ;1.  Any copy made of this software must include this copyright notice
  9. ;in full.
  10. ;
  11. ;2.  I have made no warrantee or representation that the operation of
  12. ;this software will be error-free, and I am under no obligation to
  13. ;provide any services, by way of maintenance, update, or otherwise.
  14. ;
  15. ;3.  In conjunction with products arising from the use of this
  16. ;material, there shall be no use of my name in any advertising,
  17. ;promotional, or sales literature without prior written consent in
  18. ;each case.
  19.  
  20. (require 'string-case)
  21.  
  22. ;; Parse the output of NUMBER->STRING.
  23. ;; Returns a list: (sign-character digit-string exponent-integer)
  24. ;; sign-char will be either #\+ or #\-, digit-string will always begin
  25. ;; with a "0", after which a decimal point should be understood.
  26. (define (stdio:parse-float str)
  27.   (let ((n (string-length str)))
  28.     (letrec ((prefix
  29.           (lambda (i rest)
  30.         (if (and (< i (- n 1))
  31.              (char=? #\# (string-ref str i)))
  32.             (case (string-ref str (+ i 1))
  33.               ((#\d #\i #\e) (prefix (+ i 2) rest))
  34.               ((#\.) (rest i))
  35.               (else (parse-error)))
  36.             (rest i))))
  37.          (sign
  38.           (lambda (i rest)
  39.         (if (< i n)
  40.             (let ((c (string-ref str i)))
  41.               (case c
  42.             ((#\- #\+) (cons c (rest (+ i 1))))
  43.             (else (cons #\+ (rest i))))))))
  44.          (digits 
  45.           (lambda (i rest)
  46.         (do ((j i (+ j 1)))
  47.             ((or (>= j n)
  48.              (not (or (char-numeric? (string-ref str j))
  49.                   (char=? #\# (string-ref str j)))))
  50.              (cons
  51.               (if (= i j) "0" (substring str i j))
  52.               (rest j))))))
  53.          (point
  54.           (lambda (i rest)
  55.         (if (and (< i n)
  56.              (char=? #\. (string-ref str i)))
  57.             (rest (+ i 1))
  58.             (rest i))))
  59.          (exp 
  60.           (lambda (i)
  61.         (if (< i n)
  62.             (case (string-ref str i)
  63.               ((#\e #\s #\f #\d #\l #\E #\S #\F #\D #\L)
  64.                (let ((s (sign (+ i 1) (lambda (i) (digits i end)))))
  65.              (list
  66.               (if (char=? #\- (car s))
  67.                   (- (string->number (cadr s)))
  68.                   (string->number (cadr s))))))
  69.               (else (parse-error)))
  70.             '(0))))
  71.          (end
  72.           (lambda (i)
  73.         (if (< i n) (parse-error) '())))
  74.          (parse-error
  75.           (lambda () #f)))
  76.       (let ((parsed
  77.          (prefix 0
  78.              (lambda (i)
  79.                (sign i 
  80.                  (lambda (i)
  81.                    (digits i
  82.                        (lambda (i)
  83.                      (point i
  84.                         (lambda (i)
  85.                           (digits i exp)))))))))))
  86.     (and (list? parsed)
  87.          (apply
  88.           (lambda (sgn idigs fdigs exp)
  89.         (let* ((digs (string-append "0" idigs fdigs))
  90.                (n (string-length digs)))
  91.           (let loop ((i 1)
  92.                  (exp (+ exp (string-length idigs))))
  93.             (if (and (< i n)
  94.                  (char=? #\0 (string-ref digs i)))
  95.             (loop (+ i 1) (- exp 1))
  96.             (list sgn (substring digs (- i 1) n) exp)))))
  97.           parsed))))))
  98.  
  99. ;; STR is a digit string representing a floating point mantissa, STR must
  100. ;; begin with "0", after which a decimal point is understood.
  101. ;; The output is a digit string rounded to NDIGS digits after the decimal
  102. ;; point implied between chars 0 and 1.
  103. ;; If STRIP-0S is not #F then trailing zeros will be stripped from the result.
  104. ;; In this case, STRIP-0S should be the minimum number of digits required
  105. ;; after the implied decimal point.
  106. (define (stdio:round-string str ndigs strip-0s)
  107.   (let* ((n (- (string-length str) 1))
  108.      (res
  109.       (cond ((< ndigs 0) "")
  110.         ((= n ndigs) str)
  111.         ((< n ndigs)
  112.          (let ((zeropad (make-string
  113.                  (max 0 (- (or strip-0s ndigs) n))
  114.                  (if (char-numeric? (string-ref str n))
  115.                      #\0 #\#))))
  116.            (if (zero? (string-length zeropad))
  117.                str
  118.                (string-append str zeropad))))
  119.         (else
  120.          (let ((res (substring str 0 (+ ndigs 1)))
  121.                (dig (lambda (i)
  122.                   (let ((c (string-ref str i)))
  123.                 (if (char-numeric? c)
  124.                     (string->number (string c))
  125.                     0)))))
  126.            (let ((ldig (dig (+ 1 ndigs))))
  127.              (if (or (> ldig 5)
  128.                  (and (= ldig 5)
  129.                   (let loop ((i (+ 2 ndigs)))
  130.                     (if (> i n) (odd? (dig ndigs))
  131.                     (if (zero? (dig i))
  132.                         (loop (+ i 1))
  133.                         #t)))))
  134.              (let inc! ((i ndigs))
  135.                (let ((d (dig i)))
  136.                  (if (< d 9)
  137.                  (string-set! res i 
  138.                           (string-ref
  139.                            (number->string (+ d 1)) 0))
  140.                  (begin
  141.                    (string-set! res i #\0)
  142.                    (inc! (- i 1))))))))
  143.            res)))))
  144.     (if strip-0s
  145.     (let loop ((i (- (string-length res) 1)))
  146.       (if (or (<= i strip-0s)
  147.           (not (char=? #\0 (string-ref res i))))
  148.           (substring res 0 (+ i 1))
  149.           (loop (- i 1))))
  150.     res)))
  151.  
  152.  
  153. (define (stdio:iprintf out format-string . args)
  154.   (cond
  155.    ((not (equal? "" format-string))
  156.     (let ((pos -1)
  157.       (fl (string-length format-string))
  158.       (fc (string-ref format-string 0)))
  159.  
  160.       (define (advance)
  161.     (set! pos (+ 1 pos))
  162.     (cond ((>= pos fl) (set! fc #f))
  163.           (else (set! fc (string-ref format-string pos)))))
  164.       (define (must-advance)
  165.     (set! pos (+ 1 pos))
  166.     (cond ((>= pos fl) (incomplete))
  167.           (else (set! fc (string-ref format-string pos)))))
  168.       (define (end-of-format?)
  169.     (>= pos fl))
  170.       (define (incomplete)
  171.     (slib:error 'printf "conversion specification incomplete"
  172.             format-string))
  173.       (define (wna)
  174.     (slib:error 'printf "wrong number of arguments"
  175.             (length args)
  176.             format-string))
  177.  
  178.       (let loop ((args args))
  179.     (advance)
  180.     (cond
  181.      ((end-of-format?)
  182.       (or (null? args) (wna)))
  183.      ((eqv? #\\ fc);;Emulating C strings may not be a good idea.
  184.       (must-advance)
  185.       (and (case fc
  186.          ((#\n #\N) (out #\newline))
  187.          ((#\t #\T) (out slib:tab))
  188.          ;;((#\r #\R) (out #\return))
  189.          ((#\f #\F) (out slib:form-feed))
  190.          ((#\newline) #t)
  191.          (else (out fc)))
  192.            (loop args)))
  193.      ((eqv? #\% fc)
  194.       (must-advance)
  195.       (let ((left-adjust #f)    ;-
  196.         (signed #f)        ;+
  197.         (blank #f)
  198.         (alternate-form #f)    ;#
  199.         (leading-0s #f)        ;0
  200.         (width 0)
  201.         (precision -1)
  202.         (type-modifier #f)
  203.         (read-format-number
  204.          (lambda ()
  205.            (cond
  206.             ((eqv? #\* fc)    ; GNU extension
  207.              (must-advance)
  208.              (let ((ans (car args)))
  209.                (set! args (cdr args))
  210.                ans))
  211.             (else
  212.              (do ((c fc fc)
  213.               (accum 0 (+ (* accum 10)
  214.                       (string->number (string c)))))
  215.              ((not (char-numeric? fc)) accum)
  216.                (must-advance)))))))
  217.         (define (pad pre . strs)
  218.           (let loop ((len (string-length pre))
  219.              (ss strs))
  220.         (cond ((>= len width) (apply string-append pre strs))
  221.               ((null? ss)
  222.                (cond (left-adjust
  223.                   (apply string-append
  224.                      pre
  225.                      (append strs
  226.                          (list (make-string 
  227.                             (- width len) #\space)))))
  228.                  (leading-0s
  229.                   (apply string-append
  230.                      pre
  231.                      (make-string (- width len) #\0)
  232.                      strs))
  233.                  (else
  234.                   (apply string-append
  235.                      (make-string (- width len) #\space)
  236.                      pre strs))))
  237.               (else
  238.                (loop (+ len (string-length (car ss))) (cdr ss))))))
  239.         (define integer-convert
  240.           (lambda (s radix)
  241.         (cond ((not (negative? precision))
  242.                (set! leading-0s #f)
  243.                (if (and (zero? precision)
  244.                 (eqv? 0 s))
  245.                (set! s ""))))
  246.         (set! s (cond ((symbol? s) (symbol->string s))
  247.                   ((number? s) (number->string s radix))
  248.                   ((or (not s) (null? s)) "0")
  249.                   ((string? s) s)
  250.                   (else "1")))
  251.         (let ((pre (cond ((equal? "" s) "")
  252.                  ((eqv? #\- (string-ref s 0))
  253.                   (set! s (substring s 1 (string-length s)))
  254.                   "-")
  255.                  (signed "+")
  256.                  (blank " ")
  257.                  (alternate-form
  258.                   (case radix
  259.                     ((8) "0")
  260.                     ((16) "0x")
  261.                     (else "")))
  262.                  (else ""))))
  263.           (pad pre
  264.                (if (< (string-length s) precision)
  265.                (make-string
  266.                 (- precision (string-length s)) #\0)
  267.                "")
  268.                s))))
  269.         (define (float-convert num fc)
  270.           (define (f digs exp strip-0s)
  271.         (let ((digs (stdio:round-string
  272.                  digs (+ exp precision) (and strip-0s exp))))
  273.           (cond ((>= exp 0)
  274.              (let* ((i0 (cond ((zero? exp) 0)
  275.                       ((char=? #\0 (string-ref digs 0)) 1)
  276.                       (else 0)))
  277.                 (i1 (max 1 (+ 1 exp)))
  278.                 (idigs (substring digs i0 i1))
  279.                 (fdigs (substring digs i1
  280.                           (string-length digs))))
  281.                (cons idigs
  282.                  (if (and (string=? fdigs "")
  283.                       (not alternate-form))
  284.                      '()
  285.                      (list "." fdigs)))))
  286.             ((zero? precision)
  287.              (list (if alternate-form "0." "0")))
  288.             ((and strip-0s (string=? digs "") (list "0")))
  289.             (else
  290.              (list "0."
  291.                    (make-string (min precision (- -1 exp)) #\0)
  292.                    digs)))))
  293.           (define (e digs exp strip-0s)
  294.         (let* ((digs (stdio:round-string 
  295.                   digs (+ 1 precision) (and strip-0s 0)))
  296.                (istrt (if (char=? #\0 (string-ref digs 0)) 1 0))
  297.                (fdigs (substring 
  298.                    digs (+ 1 istrt) (string-length digs)))
  299.                (exp (if (zero? istrt) exp (- exp 1))))
  300.           (list
  301.            (substring digs istrt (+ 1 istrt))
  302.            (if (and (string=? fdigs "") (not alternate-form))
  303.                "" ".")
  304.            fdigs
  305.            (if (char-upper-case? fc) "E" "e")
  306.            (if (negative? exp) "-" "+")
  307.            (if (< -10 exp 10) "0" "")
  308.            (number->string (abs exp)))))
  309.           (define (g digs exp)
  310.         (let ((strip-0s (not alternate-form)))
  311.           (set! alternate-form #f)
  312.           (cond ((< -4 exp (+ 1 precision))
  313.              (set! precision (- precision exp))
  314.              (f digs exp strip-0s))
  315.             (else
  316.              (set! precision (- precision 1))
  317.              (e digs exp strip-0s)))))
  318.           (define (k digs exp sep)
  319.         (let* ((units '#("y" "z" "a" "f" "p" "n" "u" "m" ""
  320.                  "k" "M" "G" "T" "P" "E" "Z" "Y"))
  321.                (base 8)        ;index of ""
  322.                (uind (let ((i (if (negative? exp)
  323.                       (quotient (- exp 3) 3)
  324.                       (quotient (- exp 1) 3))))
  325.                    (and
  326.                 (< -1 (+ i base) (vector-length units))
  327.                 i))))
  328.           (cond (uind
  329.              (set! exp (- exp (* 3 uind)))
  330.              (set! precision (max 0 (- precision exp)))
  331.              (append
  332.               (f digs exp #f)
  333.               (list sep
  334.                 (vector-ref units (+ uind base)))))
  335.             (else
  336.              (g digs exp)))))
  337.  
  338.           (cond ((negative? precision)
  339.              (set! precision 6))
  340.             ((and (zero? precision)
  341.               (char-ci=? fc #\g))
  342.              (set! precision 1)))
  343.           (let* ((str 
  344.               (cond ((number? num)
  345.                  (number->string (exact->inexact num)))
  346.                 ((string? num) num)
  347.                 ((symbol? num) (symbol->string num))
  348.                 (else "???")))
  349.              (parsed (stdio:parse-float str)))
  350.         (cond (parsed
  351.                (apply
  352.             (lambda (sgn digs exp)
  353.               (apply pad
  354.                  (if (char=? #\- sgn) "-"
  355.                      (if signed "+" (if blank " " "")))
  356.                  (case fc
  357.                    ((#\e #\E) (e digs exp #f))
  358.                    ((#\f #\F) (f digs exp #f))
  359.                    ((#\g #\G) (g digs exp))
  360.                    ((#\k) (k digs exp ""))
  361.                    ((#\K) (k digs exp " ")))))
  362.             parsed))
  363.               (else str))))
  364.         (do ()
  365.         ((case fc
  366.            ((#\-) (set! left-adjust #t) #f)
  367.            ((#\+) (set! signed #t) #f)
  368.            ((#\ ) (set! blank #t) #f)
  369.            ((#\#) (set! alternate-form #t) #f)
  370.            ((#\0) (set! leading-0s #t) #f)
  371.            (else #t)))
  372.           (must-advance))
  373.         (cond (left-adjust (set! leading-0s #f)))
  374.         (cond (signed (set! blank #f)))
  375.  
  376.         (set! width (read-format-number))
  377.         (cond ((negative? width)
  378.            (set! left-adjust #t)
  379.            (set! width (- width))))
  380.         (cond ((eqv? #\. fc)
  381.            (must-advance)
  382.            (set! precision (read-format-number))))
  383.         (case fc            ;Ignore these specifiers
  384.           ((#\l #\L #\h)
  385.            (set! type-modifier fc)
  386.            (must-advance)))
  387.  
  388.         ;;At this point fc completely determines the format to use.
  389.         (if (null? args)
  390.         (if (memv (char-downcase fc)
  391.               '(#\c #\s #\a #\d #\i #\u #\o #\x #\b 
  392.                 #\f #\e #\g #\k))
  393.             (wna)))
  394.  
  395.         (case fc
  396.           ;; only - is allowed between % and c
  397.           ((#\c #\C)        ; C is enhancement
  398.            (and (out (string (car args))) (loop (cdr args))))
  399.  
  400.           ;; only - flag, no type-modifiers
  401.           ((#\s #\S)        ; S is enhancement
  402.            (let ((s (cond
  403.              ((symbol? (car args)) (symbol->string (car args)))
  404.              ((not (car args)) "(NULL)")
  405.              (else (car args)))))
  406.          (cond ((not (or (negative? precision)
  407.                  (>= precision (string-length s))))
  408.             (set! s (substring s 0 precision))))
  409.          (and (out (cond
  410.                 ((<= width (string-length s)) s)
  411.                 (left-adjust
  412.                  (string-append
  413.                   s (make-string (- width (string-length s)) #\ )))
  414.                 (else
  415.                  (string-append
  416.                   (make-string (- width (string-length s))
  417.                        (if leading-0s #\0 #\ )) s))))
  418.               (loop (cdr args)))))
  419.  
  420.           ;; SLIB extension
  421.           ((#\a #\A)        ;#\a #\A are pretty-print
  422.            (require 'generic-write)
  423.            (let ((os "") (pr precision))
  424.          (generic-write
  425.           (car args) (not alternate-form) #f
  426.           (cond ((and left-adjust (negative? pr))
  427.              (set! pr 0)
  428.              (lambda (s)
  429.                (set! pr (+ pr (string-length s)))
  430.                (out s)))
  431.             (left-adjust
  432.              (lambda (s)
  433.                (define sl (- pr (string-length s)))
  434.                (set! pr (cond ((negative? sl)
  435.                        (out (substring s 0 pr)) 0)
  436.                       (else (out s) sl)))
  437.                (positive? sl)))
  438.             ((negative? pr)
  439.              (set! pr width)
  440.              (lambda (s)
  441.                (set! pr (- pr (string-length s)))
  442.                (cond ((not os) (out s))
  443.                  ((negative? pr)
  444.                   (out os)
  445.                   (set! os #f)
  446.                   (out s))
  447.                  (else (set! os (string-append os s))))
  448.                #t))
  449.             (else
  450.              (lambda (s)
  451.                (define sl (- pr (string-length s)))
  452.                (cond ((negative? sl)
  453.                   (set! os (string-append
  454.                         os (substring s 0 pr))))
  455.                  (else (set! os (string-append os s))))
  456.                (set! pr sl)
  457.                (positive? sl)))))
  458.          (cond ((and left-adjust (negative? precision))
  459.             (cond
  460.              ((> width pr) (out (make-string (- width pr) #\ )))))
  461.                (left-adjust
  462.             (cond
  463.              ((> width (- precision pr))
  464.               (out (make-string (- width (- precision pr)) #\ )))))
  465.                ((not os))
  466.                ((<= width (string-length os)) (out os))
  467.                (else (and (out (make-string
  468.                     (- width (string-length os)) #\ ))
  469.                   (out os)))))
  470.            (loop (cdr args)))
  471.           ((#\d #\D #\i #\I #\u #\U)
  472.            (and (out (integer-convert (car args) 10)) (loop (cdr args))))
  473.           ((#\o #\O)
  474.            (and (out (integer-convert (car args) 8)) (loop (cdr args))))
  475.           ((#\x #\X)
  476.            (and (out ((if (char-upper-case? fc)
  477.                   string-upcase string-downcase)
  478.               (integer-convert (car args) 16)))
  479.             (loop (cdr args))))
  480.           ((#\b #\B)
  481.            (and (out (integer-convert (car args) 2)) (loop (cdr args))))
  482.           ((#\%) (and (out #\%) (loop args)))
  483.           ((#\f #\F #\e #\E #\g #\G #\k #\K)
  484.            (and (out (float-convert (car args) fc)) (loop (cdr args))))
  485.           (else
  486.            (cond ((end-of-format?) (incomplete))
  487.              (else (and (out #\%) (out fc) (out #\?) (loop args))))))))
  488.      (else (and (out fc) (loop args)))))))))
  489.  
  490. (define (stdio:fprintf port format . args)
  491.   (let ((cnt 0))
  492.     (apply stdio:iprintf
  493.        (lambda (x)
  494.          (cond ((string? x)
  495.             (set! cnt (+ (string-length x) cnt)) (display x port) #t)
  496.            (else (set! cnt (+ 1 cnt)) (display x port) #t)))
  497.        format args)
  498.     cnt))
  499.  
  500. (define (stdio:printf format . args)
  501.   (apply stdio:fprintf (current-output-port) format args))
  502.  
  503. (define (stdio:sprintf str format . args)
  504.   (let* ((cnt 0)
  505.      (s (cond ((string? str) str)
  506.           ((number? str) (make-string str))
  507.           ((not str) (make-string 100))
  508.           (else (slib:error 'sprintf "first argument not understood"
  509.                     str))))
  510.      (end (string-length s)))
  511.     (apply stdio:iprintf
  512.        (lambda (x)
  513.          (cond ((string? x)
  514.             (if (or str (>= (- end cnt) (string-length x)))
  515.             (do ((lend (min (string-length x) (- end cnt)))
  516.                  (i 0 (+ i 1)))
  517.                 ((>= i lend))
  518.               (string-set! s cnt (string-ref x i))
  519.               (set! cnt (+ cnt 1)))
  520.             (let ()
  521.               (set! s (string-append (substring s 0 cnt) x))
  522.               (set! cnt (string-length s))
  523.               (set! end cnt))))
  524.            ((and str (>= cnt end)))
  525.            (else (cond ((and (not str) (>= cnt end))
  526.                 (set! s (string-append s (make-string 100)))
  527.                 (set! end (string-length s))))
  528.              (string-set! s cnt (if (char? x) x #\?))
  529.              (set! cnt (+ cnt 1))))
  530.          (not (and str (>= cnt end))))
  531.        format
  532.        args)
  533.     (cond ((string? str) cnt)
  534.       ((eqv? end cnt) s)
  535.       (else (substring s 0 cnt)))))
  536.  
  537. (define printf stdio:printf)
  538. (define fprintf stdio:fprintf)
  539. (define sprintf stdio:sprintf)
  540.  
  541. ;;(do ((i 0 (+ 1 i))) ((> i 50)) (printf "%s\n" (sprintf i "%#-13a:%#13a:%-13.8a:" "123456789" "123456789" "123456789")))
  542.